﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Ink;
using UMLTouch.Core.UI;
using System.Windows;
using UMLTouch.Core.Models;
using System.Windows.Input;
using UMLTouch.Core.Commands;

namespace UMLTouch.Core.ViewModel
{
    public abstract class DiagramContextViewModel : ViewModelBase
    {

        Boolean _selectionMode;

        public Boolean SelectionMode
        {
            get { return _selectionMode; }
            set { 
                _selectionMode = value;
                RaisePropertyChanged("SelectionMode");
            }
        }

        public ICommand SwitchEditModeCmd
        {
            get;
            set;
        }

        public virtual IDiagramContext DiagramContext { get; protected set; }

        DiagramItemsViewModelCollection _diagramItems;

        public DiagramItemsViewModelCollection DiagramItems
        {
            get { return _diagramItems; }
            set {
                _diagramItems = value;
                
            }
        }


        public String Name
        {
            get
            {
                return DiagramContext.Name;
            }
            set
            {
                if (value == DiagramContext.Name)
                    return;

                DiagramContext.Name = value;
                RaisePropertyChanged("Name");
            }
        }
        public String Path
        {
            get
            {
                return DiagramContext.Path;
            }
            set
            {
                if (value == DiagramContext.Path)
                    return;

                DiagramContext.Path = value;
                RaisePropertyChanged("Path");
            }
        }


        public StrokeCollection Strokes { get; protected set; }



        public DiagramContextViewModel(IDiagramContext diagramContext, DiagramItemsViewModelCollection diagramItems)
            :this()
        {
            DiagramContext = diagramContext;
            DiagramItems = diagramItems;
            Strokes = new StrokeCollection();
            Strokes.StrokesChanged += new StrokeCollectionChangedEventHandler(Strokes_StrokesChanged);
            DiagramContext.DiagramItems.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_diagramItems_CollectionChanged);
        }


        private DiagramContextViewModel()
            :base()
        {
            SwitchEditModeCmd = new RelayCommand(action => SelectionMode = !SelectionMode);
        }

        protected virtual DiagramItemsViewModelCollection CreateDiagramItemsCollection()
        {
            DiagramItemsViewModelCollection diagramItemsViewModelCollection = new DiagramItemsViewModelCollection();
            return diagramItemsViewModelCollection;
        }


        protected virtual void Strokes_StrokesChanged(object sender, StrokeCollectionChangedEventArgs e)
        {
            foreach (var stroke in e.Added)
            {
                UMLTouch.Core.Entities.Stroke s = new UMLTouch.Core.Entities.Stroke(stroke);
                DiagramContext.InputStroke(s);
            }
        }

        protected virtual void _diagramItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            Application.Current.Dispatcher.Invoke(
                new Action(() => 
                {
                    foreach (IDiagramItem item in e.NewItems)
                    {
                        AddDiagramItem(item);
                    }
                })
                );
        }

        protected abstract DiagramItemViewModel CreateDiagramItemViewModel(IDiagramItem item);



        protected void AddDiagramItem(IDiagramItem diagramItem)
        {

            BindAnchors(diagramItem);

            DiagramItemViewModel ItemViewModel = CreateDiagramItemViewModel(diagramItem);
            DiagramItems.Add(ItemViewModel);
        }

    
        protected virtual void BindAnchors(IDiagramItem diagramItem)
        {
            if(diagramItem is ILinkItem)
            {
                ILinkItem l = diagramItem as ILinkItem;

                var q = (from availableSlot in DiagramContext.DiagramItems.OfType<ISlotItem>().SelectMany(item => item.Slots)
                        from plugs in l.Plugs
                        let dist = Math.Abs((plugs.Position - availableSlot.Position).Length)
                        where dist < availableSlot.LinkRadius
                        orderby dist
                        select new { plugs, availableSlot } into tuple
                        group tuple by tuple.plugs 
                        ).ToList();

                foreach (var plug in q)
                {
                    var r = plug.FirstOrDefault();
                    if (r != null)
                    {
                        var destSlot = r.availableSlot;
                        plug.Key.Slot = destSlot;
                    }
                }
                       
            }

            if(diagramItem is ISlotItem)
            {
                ISlotItem l = diagramItem as ISlotItem;

                var q = (from availablePlugs in DiagramContext.DiagramItems.OfType<ILinkItem>().SelectMany(item => item.Plugs).Where(plug => plug.Slot == null)
                         from slots in l.Slots
                         let dist = Math.Abs((slots.Position - availablePlugs.Position).Length)
                         where dist < availablePlugs.LinkRadius
                         orderby dist
                         select new { slots, availablePlugs } into tuple
                         group tuple by tuple.slots
                        );

                foreach (var slot in q)
                {
                    foreach (var availablePlug in slot.Select(tuple=>tuple.availablePlugs))
                    {
                        availablePlug.Slot = slot.Key;
                    }
                }
            }
        }


    }
}
